package org.sample.springbootasync.log;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

@ControllerAdvice
@ConditionalOnProperty(name = "api.log", havingValue = "true")
public class ResponseBodyLogger implements ResponseBodyAdvice<Object> {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(ResponseBodyLogger.class);

	private final ObjectMapper objectMapper;

	public ResponseBodyLogger() {
		objectMapper = new ObjectMapper();
		objectMapper.setSerializationInclusion(Include.NON_NULL);
	}

	@Override
	public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
		RequestMapping isHTTPRequest = returnType.getMethodAnnotation(RequestMapping.class);
		return isHTTPRequest != null;
	}

	@Override
	public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
			Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
			ServerHttpResponse response) {
		
		if (body != null) {
			String jsonBody;
			try {
				jsonBody = objectMapper.writeValueAsString(body);
			} catch (JsonProcessingException e) {
				LOGGER.warn("Convert response to JSON failed", e);
				jsonBody = null;
			}
			
			if (jsonBody == null) {
				jsonBody = body.toString();
			}
			
			LOGGER.info("HTTP response of {}.{}", returnType.getMethod().getDeclaringClass().getName(),
					returnType.getMethod().getName());
			LOGGER.info(jsonBody);
		}
		
		return body;
	}
}
